home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / examples / object / trackball__define.pro < prev    next >
Text File  |  1997-07-08  |  14KB  |  447 lines

  1. ; $Id: trackball__define.pro,v 1.3 1997/04/18 00:46:51 griz Exp $
  2. ;
  3. ; Copyright (c) 1997, Research Systems, Inc.  All rights reserved.
  4. ;    Unauthorized reproduction prohibited.
  5. ;+
  6. ; NAME:
  7. ;    TRACKBALL
  8. ;
  9. ; PURPOSE:
  10. ;    This object translates widget events for draw widgets into 
  11. ;       transformations that emulate a virtual trackball (for transforming
  12. ;       object graphics in three dimensions).  
  13. ;
  14. ; CATEGORY:
  15. ;    Object Graphics.
  16. ;
  17. ; CALLING SEQUENCE:
  18. ;    To initially create:
  19. ;        oTrackball = OBJ_NEW('Trackball', Center, Radius)
  20. ;
  21. ;    To update the trackball state based on a widget event:
  22. ;        oTrackball->Update, sEvent
  23. ;
  24. ;    To re-initialize the trackball state:
  25. ;        oTrackball->Reset, Center, Radius
  26. ;
  27. ;    To destroy:
  28. ;        OBJ_DESTROY, oTrackball
  29. ;
  30. ; INPUTS:
  31. ;   TRACKBALL::INIT:
  32. ;    Center:    A two-dimensional vector, [x,y], representing the requested
  33. ;        center (measured in device units) of the trackball.
  34. ;       Radius:    The requested radius (measured in device units) of the
  35. ;         trackball.
  36. ;
  37. ;   TRACKBALL::UPDATE:
  38. ;        sEvent: The widget event structure.  The event type indicates 
  39. ;        how the trackball state should be updated.
  40. ;
  41. ;   TRACKBALL::RESET:
  42. ;    Center:    A two-dimensional vector, [x,y], representing the requested
  43. ;        center (measured in device units) of the trackball.
  44. ;       Radius:    The requested radius (measured in device units) of the
  45. ;         trackball.
  46. ;
  47. ; KEYWORD PARAMETERS:
  48. ;   TRACKBALL::INIT:
  49. ;    AXIS:        Set this keyword to indicate the axis about which
  50. ;            rotations are to be constrained if the CONSTRAIN
  51. ;            keyword is set to a nonzer value.  Valid values
  52. ;            include:
  53. ;                0 = X-Axis
  54. ;                1 = Y-Axis
  55. ;                2 = Z-Axis (default)
  56. ;    CONSTRAIN:    Set this keyword to a nonzero value to indicate that
  57. ;            the trackball transformations are to be constrained
  58. ;            about a given axis (as specified by the AXIS 
  59. ;            keyword).  The default is zero (no constraints).
  60. ;    MOUSE:        Set this keyword to a bitmask to indicate which
  61. ;            mouse button to honor for trackball events.  The
  62. ;            least significant bit represents the leftmost
  63. ;            button, the next highest bit represents the middle
  64. ;            button, and the next highest bit represents the
  65. ;            right button.  The default is 1b, for the left
  66. ;            mouse button.
  67. ;
  68. ;   TRACKBALL::UPDATE:
  69. ;    TRANSFORM:    Set this keyword to a named variable that upon
  70. ;            return will contain a floating point 4x4 array
  71. ;            if a transformations matrix is calculated as
  72. ;            a result of the widget event.
  73. ;
  74. ;   TRACKBALL::RESET:
  75. ;    AXIS:        Set this keyword to indicate the axis about which
  76. ;            rotations are to be constrained if the CONSTRAIN
  77. ;            keyword is set to a nonzer value.  Valid values
  78. ;            include:
  79. ;                0 = X-Axis
  80. ;                1 = Y-Axis
  81. ;                2 = Z-Axis (default)
  82. ;    CONSTRAIN:    Set this keyword to a nonzero value to indicate that
  83. ;            the trackball transformations are to be constrained
  84. ;            about a given axis (as specified by the AXIS 
  85. ;            keyword).  The default is zero (no constraints).
  86. ;    MOUSE:        Set this keyword to a bitmask to indicate which
  87. ;            mouse button to honor for trackball events.  The
  88. ;            least significant bit represents the leftmost
  89. ;            button, the next highest bit represents the middle
  90. ;            button, and the next highest bit represents the
  91. ;            right button.  The default is 1b, for the left
  92. ;            mouse button.
  93. ;
  94. ; OUTPUTS:
  95. ;   TRACKBALL::UPDATE:
  96. ;    This function returns a 1 if a transformation matrix is calculated
  97. ;    as a result of the widget event, or 0 otherwise.
  98. ;
  99. ; EXAMPLE:
  100. ;    Create a trackball centered on a 512x512 pixel drawable area, and
  101. ;    a view containing the model to be manipulated:    
  102. ;        xdim = 512
  103. ;        ydim = 512
  104. ;        wBase = WIDGET_BASE()
  105. ;        wDraw = WIDGET_DRAW(wBase, XSIZE=xdim, YSIZE=ydim, $
  106. ;                            GRAPHICS_LEVEL=2, /BUTTON_EVENTS, $
  107. ;                            /MOTION_EVENTS, /EXPOSE_EVENTS, RETAIN=0 )
  108. ;        WIDGET_CONTROL, wBase, /REALIZE
  109. ;        WIDGET_CONTROL, wDraw, GET_VALUE=oWindow
  110. ;
  111. ;        oTrackball = OBJ_NEW('Trackball', [xdim/2.,ydim/2.], xdim/2.)
  112. ;        oView = OBJ_NEW('IDLgrView')
  113. ;        oModel = OBJ_NEW('IDLgrModel')
  114. ;        oView->Add, oModel
  115. ;
  116. ;        XMANAGER, 'TrackEx', wBase
  117. ;
  118. ;    In the widget event handler, handle trackball updates.
  119. ;    As the trackball transformation changes, update the transformation
  120. ;    for a model object (instance of IDLgrModel), and redraw the view:
  121. ;
  122. ;    PRO TrackEx_Event, sEvent
  123. ;        ...
  124. ;        bHaveXform = oTrackball->Update( sEvent, TRANSFORM=TrackXform )
  125. ;        IF (bHaveXform) THEN BEGIN
  126. ;            oModel->GetProperty, TRANSFORM=ModelXform
  127. ;            oModel->SetProperty, TRANSFORM=ModelXform # TrackXform
  128. ;            oWindow->Draw, oView
  129. ;        ENDIF
  130. ;        ...
  131. ;    END
  132. ;
  133. ; MODIFICATION HISTORY:
  134. ;     Written by:    DD, December 1996
  135. ;
  136. ;-
  137.  
  138. ;----------------------------------------------------------------------------
  139. ; TRACKBALL_CONSTRAIN
  140. ;
  141. ; Purpose:
  142. ;  Given a point and a constraint vector, map the point to its constrained
  143. ;  equivalent.
  144. ;
  145. ; Arguments:
  146. ;  pt - The unconstrained point.
  147. ;  vec - A three-element vector, [x,y,z], representing the unit vector about
  148. ;        which rotations are constrained.
  149. ;
  150. FUNCTION TRACKBALL_CONSTRAIN, pt, vec
  151.     ; Project the point.
  152.     proj = pt - TOTAL(vec * pt) * vec
  153.  
  154.     ; Normalizing factor.
  155.     norm = SQRT(TOTAL(proj^2))
  156.  
  157.     IF (norm GT 0.0) THEN BEGIN
  158.         IF (proj[2] LT 0.0) THEN $
  159.             cpt = -1.0 / norm * proj $
  160.         ELSE $
  161.             cpt = 1.0 / norm * proj
  162.     ENDIF ELSE IF vec[2] EQ 1.0 THEN $
  163.         cpt = [1.0, 0.0, 0.0] $
  164.     ELSE $
  165.         cpt = [-vec[1], vec[0], 0.0] / SQRT(TOTAL(vec[0:1]^2)) 
  166.  
  167.     RETURN, cpt
  168. END
  169.  
  170. ;----------------------------------------------------------------------------
  171. ; TRACKBALL::UPDATE
  172. ;
  173. ; Purpose:
  174. ;  Given a widget event structure, updates the trackball state.
  175. ;
  176. ;  The return value is nonzero if a transformation matrix is calculated
  177. ;  as a result of the event, or zero otherwise.
  178. ;
  179. ; Arguments:
  180. ;  sEvent - The widget event structure.
  181. ;                  
  182. ; Keywords:
  183. ;  TRANSFORM - If a transformation matrix is calculated, upon return
  184. ;              transform will contain a 4x4 matrix.
  185. ;
  186. FUNCTION TRACKBALL::UPDATE, sEvent, TRANSFORM=transform
  187.   ; Initialize return value. 
  188.   bHaveTransform=0
  189.  
  190.   ; Ignore non-Draw-Widget events.
  191.   IF (TAG_NAMES(sEvent, /STRUCTURE_NAME) NE 'WIDGET_DRAW') THEN $
  192.     RETURN, bHaveTransform
  193.  
  194.   ; Determine event type.
  195.   CASE sEvent.type OF
  196.     0: BEGIN    ;Button press.
  197.          ; Only handle event if appropriate mouse button.
  198.          IF ((sEvent.press AND self.mouse) EQ sEvent.press) THEN BEGIN
  199.            ; Calculate distance of mouse click from center of unit circle.
  200.            xy = ([sEvent.x,sEvent.y] - self.center) / self.radius
  201.            r = TOTAL(xy^2)
  202.            IF (r GT 1.0) THEN $
  203.              self.pt1 = [xy/SQRT(r) ,0.0] $
  204.            ELSE $
  205.              self.pt1 = [xy,SQRT(1.0-r)]
  206.  
  207.            ; Constrain if necessary.
  208.            IF (self.constrain NE 0) THEN BEGIN
  209.                vec = [0.,0.,0.]
  210.                vec[self.axis] = 1.0
  211.                self.pt1 = TRACKBALL_CONSTRAIN( self.pt1, vec)
  212.            ENDIF
  213.  
  214.            self.pt0 = self.pt1
  215.            self.btndown = 1b
  216.          ENDIF
  217.        END
  218.  
  219.     2: BEGIN    ;Button motion.
  220.          IF (self.btndown EQ 1b) THEN BEGIN
  221.            ; Calculate distance of mouse click from center of unit circle.
  222.            xy = ([sEvent.x,sEvent.y] - self.center) / $
  223.                 self.radius
  224.            r = TOTAL(xy^2)
  225.            IF (r GT 1.0) THEN $
  226.              pt1 = [xy/SQRT(r) ,0.0] $
  227.            ELSE $
  228.              pt1 = [xy,SQRT(1.0-r)]
  229.  
  230.            ; Constrain if necessary.
  231.            IF (self.constrain NE 0) THEN BEGIN
  232.                vec = [0.,0.,0.]
  233.                vec[self.axis] = 1.0
  234.                pt1 = TRACKBALL_CONSTRAIN( pt1, vec)
  235.            ENDIF
  236.  
  237.            ; Update the transform only if the mouse button has actually
  238.            ; moved from its previous location. 
  239.            pt0 = self.pt0
  240.            IF ((pt0[0] NE pt1[0]) OR $
  241.                (pt0[1] NE pt1[1]) OR $
  242.                (pt0[2] NE pt1[2])) THEN BEGIN
  243.              ; Compute transformation.
  244.              q = [CROSSP(pt0,pt1), TOTAL(pt0*pt1)]
  245.  
  246.              x = q[0]
  247.              y = q[1]
  248.              z = q[2]
  249.              w = q[3]
  250.              transform = [[ w^2+x^2-y^2-z^2, 2*(x*y-w*z), 2*(x*z+w*y), 0], $
  251.                           [ 2*(x*y+w*z), w^2-x^2+y^2-z^2, 2*(y*z-w*x), 0], $
  252.                           [ 2*(x*z-w*y), 2*(y*z+w*x), w^2-x^2-y^2+z^2, 0], $
  253.                           [ 0          , 0          , 0              , 1]]
  254.  
  255.              bHaveTransform = 1b
  256.              self.pt0 = pt1
  257.            ENDIF
  258.  
  259.            self.pt1 = pt1
  260.          ENDIF
  261.        END
  262.  
  263.     1: BEGIN    ;Button Release. 
  264.          IF (self.btndown EQ 1b) THEN $
  265.            self.btndown = 0b
  266.        END
  267.  
  268.     ELSE: RETURN, bHaveTransform
  269.  
  270.    ENDCASE
  271.  
  272.   RETURN, bHaveTransform
  273. END
  274.  
  275. ;----------------------------------------------------------------------------
  276. ; TRACKBALL::INIT
  277. ;
  278. ; Purpose:
  279. ;   Initializes the trackball state.
  280. ;
  281. ; Arguments:
  282. ;  center - A two-dimensional vector, [x,y], representing the requested 
  283. ;           center (measured in device units) of the trackball.
  284. ;  radius - The requested radius (measured in device units) of the trackball.
  285. ;
  286. ; Keywords:
  287. ;  AXIS -  Set this keyword to indicate the axis about which rotations 
  288. ;          are to be constrained if the CONSTRAIN keyword is set to a
  289. ;          nonzero value).  Valid values include:  
  290. ;               0 = X
  291. ;               1 = Y
  292. ;               2 = Z (default)
  293. ;  CONSTRAIN - Set this keyword to a nonzero value to indicate that the 
  294. ;          trackball transformations are to be constrained about a given 
  295. ;          axis (as specified by the AXIS keyword).  The default is zero
  296. ;          (no constraints). 
  297. ;  MOUSE - Set this keyword to a bitmask to indicate which mouse button to
  298. ;          honor for trackball events.  The least significant bit represents
  299. ;          the leftmost button, the next highest bit represents the middle
  300. ;          button, and the next highest bit represents the right button.
  301. ;          The default is 1b, for the left moust button.
  302. ;
  303. FUNCTION TRACKBALL::INIT, center, radius, AXIS=axis, CONSTRAIN=constrain, $
  304.                           MOUSE=mouse
  305.     IF (N_ELEMENTS(center) NE 2) THEN BEGIN
  306.         PRINT, 'Trackball: center must be a two-dimensional array.'
  307.         RETURN, 0
  308.     ENDIF
  309.  
  310.     IF (N_ELEMENTS(radius) NE 1) THEN BEGIN
  311.         PRINT, 'Trackball: invalid radius.'
  312.         RETURN, 0
  313.     ENDIF
  314.  
  315.     IF (N_ELEMENTS(axis) NE 0) THEN BEGIN
  316.         IF (axis < 0) OR (axis > 2) THEN BEGIN
  317.             PRINT, 'Trackball: invalid value for AXIS keyword.'
  318.             RETURN, 0
  319.         ENDIF ELSE $
  320.             self.axis = axis
  321.     ENDIF ELSE $
  322.         self.axis = 2
  323.  
  324.     IF (N_ELEMENTS(constrain) NE 0) THEN $
  325.         self.constrain = constrain $
  326.     ELSE $
  327.         self.constrain = 0
  328.          
  329.     IF (N_ELEMENTS(mouse) NE 0) THEN BEGIN
  330.         IF (NOT (((mouse AND 1) EQ 1) OR $
  331.                  ((mouse AND 2) EQ 2) OR $
  332.                  ((mouse AND 4) EQ 4))) THEN BEGIN
  333.             PRINT, 'Trackball: invalid value for MOUSE keyword.'
  334.             RETURN, 0
  335.         ENDIF ELSE $
  336.             self.mouse = mouse  
  337.     ENDIF ELSE $
  338.         self.mouse = 1 ; Default is left mouse button
  339.  
  340.     self.center = center
  341.     self.radius = radius 
  342.  
  343.     self.btndown = 0b
  344.  
  345.     RETURN, 1
  346. END
  347.  
  348. ;----------------------------------------------------------------------------
  349. ; TRACKBALL::CLEANUP
  350. ;
  351. ; Purpose:
  352. ;   Cleanup the trackball when it is destroyed.
  353. ;
  354. ; Arguments:
  355. ;  <None>
  356. ;
  357. ; Keywords:
  358. ;  <None>
  359. ;
  360. PRO TRACKBALL::CLEANUP
  361.     ; No work needs to be done.  We provide this method to avoid any
  362.     ; problems resolving the Cleanup method call on Windows 3.11
  363.     ; which has short filename restrictions.
  364. END
  365.  
  366. ;----------------------------------------------------------------------------
  367. ; TRACKBALL::RESET
  368. ;
  369. ; Purpose:
  370. ;   Resets the trackball state.
  371. ;
  372. ; Arguments:
  373. ;  center - A two-dimensional vector, [x,y], representing the requested 
  374. ;           center (measured in device units) of the trackball.
  375. ;  radius - The requested radius (measured in device units) of the trackball.
  376. ;
  377. ; Keywords:
  378. ;  MOUSE - Set this keyword to a bitmask to indicate which mouse button to
  379. ;          honor for trackball events.  The least significant bit represents
  380. ;          the leftmost button, the next highest bit represents the middle
  381. ;          button, and the next highest bit represents the right button.
  382. ;          The default is 1b, for the left moust button.
  383. ;
  384. PRO TRACKBALL::RESET, center, radius, AXIS=axis, CONSTRAIN=constrain, $
  385.                       MOUSE=mouse
  386.     IF (N_ELEMENTS(center) NE 2) THEN BEGIN
  387.         PRINT, 'Trackball: center must be a two-dimensional array.'
  388.         RETURN
  389.     ENDIF
  390.  
  391.     IF (N_ELEMENTS(radius) NE 1) THEN BEGIN
  392.         PRINT, 'Trackball: invalid radius.'
  393.         RETURN
  394.     ENDIF
  395.  
  396.     IF (N_ELEMENTS(axis) NE 0) THEN BEGIN
  397.         IF (axis < 0) OR (axis > 2) THEN BEGIN
  398.             PRINT, 'Trackball: invalid value for AXIS keyword.'
  399.             RETURN
  400.         ENDIF ELSE $
  401.             self.axis = axis
  402.     ENDIF ELSE $
  403.         self.axis = 2
  404.  
  405.     IF (N_ELEMENTS(constrain) NE 0) THEN $
  406.         self.constrain = constrain $
  407.     ELSE $
  408.         self.constrain = 0
  409.          
  410.     IF (N_ELEMENTS(mouse) NE 0) THEN BEGIN
  411.         IF (NOT (((mouse AND 1) EQ 1) OR $
  412.                  ((mouse AND 2) EQ 2) OR $
  413.                  ((mouse AND 4) EQ 4))) THEN BEGIN
  414.             PRINT, 'Trackball: invalid value for MOUSE keyword.'
  415.             RETURN
  416.         ENDIF ELSE $
  417.             self.mouse = mouse  
  418.     ENDIF ELSE $
  419.         self.mouse = 1 ; Default is left mouse button
  420.  
  421.     self.center = center
  422.     self.radius = radius 
  423.  
  424.     self.btndown = 0b
  425.  
  426. END
  427.  
  428. ;----------------------------------------------------------------------------
  429. ; TRACKBALL__DEFINE
  430. ;
  431. ; Purpose:
  432. ;  Defines the object structure for a trackball object.
  433. ;
  434. PRO trackball__define
  435.  
  436.   struct = {trackball, $
  437.             btndown: 0b,    $ 
  438.             axis: 0, $
  439.             constrain: 0b, $
  440.             mouse: 0b, $
  441.             center: LONARR(2), $
  442.             radius: 0.0, $
  443.             pt0: FLTARR(3), $
  444.             pt1: FLTARR(3) $
  445.            }
  446. END
  447.